﻿#include "OiPCH.hpp"
#include <QGraphicsScene>
#include <QtCharts/QValueAxis>

#define LTVIEW_SCALE 1.09


namespace Oi
{
    namespace PLScannerCore
    {
        class CoordinateHint : public QGraphicsTextItem
        {
        public:
            CoordinateHint() : QGraphicsTextItem()
            {
                
            }

            void setPoint(const QPoint& point)
            {
                setPos(point + QPointF(15, -(15 + boundingRect().height())));
                _currentPosition = mapFromScene(point);
            }

        protected:
            void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override
            {
                QGraphicsTextItem::paint(painter, option, widget);
                auto r = boundingRect();

                painter->setPen(QPen(Qt::white,1));
                painter->setBrush(QBrush(Qt::white));
                painter->drawLine(r.bottomLeft(), r.bottomRight());

                painter->drawLine(_currentPosition, r.bottomLeft());
                painter->drawEllipse(_currentPosition, 2, 2);
            }

        private:
            QPointF _currentPosition;
        };

        class CLTProfileViewImpl : public Impl<CLTProfileView>
        {
        public:
            void init();
            void exit();

        public:
            Appl::Chart* _chart = NULL;
            QGraphicsScene* _scene;
            QWidget* _viewport;

        public:
            bool _isDrag;
            QPoint _dragPoint;
            CoordinateHint* _pos;

            QString _displayName = QString();

        public:
            QList<LTBase::DefectTool> _defectTools;
            QList<int> _defectCount;
        };

        void CLTProfileViewImpl::init()
        {
            OI_Q(CLTProfileView);

            _isDrag = false;

            _scene = new QGraphicsScene();
            _scene->setBackgroundBrush(QColor(37,37,38));
            q->setScene(_scene);
            q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
            q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

            _chart = new Appl::Chart();
            _chart->legend()->hide();
            _chart->setFlag(QGraphicsItem::ItemIsSelectable, true);

            QtCharts::QValueAxis* pXAxis = new QtCharts::QValueAxis();
            pXAxis->setRange(0, 100);
            _chart->setAxisX(pXAxis);

            QtCharts::QValueAxis* pYAxis = new QtCharts::QValueAxis();
            pYAxis->setRange(0, 100);
            _chart->setAxisY(pYAxis);

            _chart->layout()->setContentsMargins(0, 0, 0, 0);
            _chart->setBackgroundRoundness(0);

            _chart->axisX()->setGridLineVisible(false);
            _chart->axisY()->setGridLineVisible(false);
            _chart->axisX()->setMinorGridLineVisible(true);

            _scene->addItem(_chart);
            _scene->setSceneRect(1.0 * INT_MIN, 1.0 * INT_MIN, 2.0 * INT_MAX, 2.0 * INT_MAX);

            q->setRenderHint(QPainter::Antialiasing);

            _viewport = new QWidget(q);

            _pos = new CoordinateHint;
            _scene->addItem(_pos);
            _pos->setVisible(false);


            q->setViewport(_viewport);
            q->setDragMode(QGraphicsView::NoDrag);
            q->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        }

        void CLTProfileViewImpl::exit()
        {

        }

        CLTProfileView::CLTProfileView(QWidget *parent)
            : QGraphicsView(parent)
        {
            OI_I(CLTProfileView)->init();
        }

        CLTProfileView::~CLTProfileView()
        {
            OI_I(CLTProfileView)->exit();
        }

        Appl::Chart* CLTProfileView::chart()
        {
            return OI_IMPL()->_chart;
        }

        void CLTProfileView::zoomFit()
        {
            OI_F(CLTProfileView);
            d->_chart->setGeometry(0, 0, viewport()->width(), viewport()->height());
            fitInView(d->_chart, Qt::KeepAspectRatio);
        }

        void CLTProfileView::resizeEvent(QResizeEvent *event)
        {
            OI_F(CLTProfileView);
            zoomFit();
            OI_IMPL()->_chart->autoRange(0.4, OI_ALL_TYPE);
            //d->_chart->saveSetting(LT360X::instance()->projectDir + "/" + LT360X::instance()->formula() + "/" + "config.ini");
        }

        void CLTProfileView::mousePressEvent(QMouseEvent *event)
        {
            OI_F(CLTProfileView);
            if(event->button() & Qt::MiddleButton)
            {
                setCursor(Qt::OpenHandCursor);
                d->_isDrag = true;
                d->_dragPoint = event->pos();

                event->accept();
                return;
            }

            if (event->button() & Qt::RightButton)
            {
                d->_pos->setPlainText(QString("(%1, %2)").arg(QString::number(d->_chart->mapToValue(event->pos()).x(), 'f', 2)).arg(QString::number(d->_chart->mapToValue(event->pos()).y(), 'f', 2)));
                d->_pos->setPoint(event->pos());
                d->_pos->setVisible(true);
                setCursor(Qt::BlankCursor);
            }

            QGraphicsView::mousePressEvent(event);
        }

        void CLTProfileView::mouseMoveEvent(QMouseEvent* event)
        {
            OI_F(CLTProfileView);
            
            if (d->_pos->isVisible())
            {
                d->_pos->setPlainText(QString("(%1, %2)").arg(QString::number(d->_chart->mapToValue(event->pos()).x(), 'f', 2)).arg(QString::number(d->_chart->mapToValue(event->pos()).y(), 'f', 2)));
                d->_pos->setPoint(event->pos());
            }

            if( event->buttons() & Qt::MiddleButton )
            {
                if(d->_isDrag)
                {
                    d->_chart->scroll(d->_dragPoint.x() - event->x(), event->y() - d->_dragPoint.y());
                    d->_dragPoint = event->pos();

                    event->accept();
                    return;
                }
            }

            QGraphicsView::mouseMoveEvent(event);
        }

        void CLTProfileView::mouseReleaseEvent(QMouseEvent* event)
        {
            OI_F(CLTProfileView);

            if(event->button() & Qt::MiddleButton)
            {
                setCursor(Qt::ArrowCursor);
                d->_isDrag = false;

                event->accept();
                return;
            }

            if (event->button() & Qt::RightButton)
            {
                d->_pos->setVisible(false);
                setCursor(Qt::ArrowCursor);
            }

            QGraphicsView::mouseReleaseEvent(event);
        }

        void CLTProfileView::mouseDoubleClickEvent(QMouseEvent* event)
        {
            OI_F(CLTProfileView);

            if (event->button() & Qt::MouseEventCreatedDoubleClick)
            {
                d->_chart->autoRange(0.4, OI_ALL_TYPE);
            }

            QGraphicsView::mouseDoubleClickEvent(event);
        }

        inline void confirmCorner(const QList<QPointF>& rect, QPointF& tl, QPointF& tr, QPointF& bl, QPointF& br)
        {
            if (rect.size() != 4)
            {
                return;
            }

            std::vector<QPointF> copy;
            for (int i = 0; i < rect.size(); i++)
            {
                copy.push_back(rect[i]);
            }
            std::sort(copy.begin(), copy.end(), [](const QPointF& pt1, const QPointF& pt2) {return pt1.x() < pt2.x(); });

            if (copy[0].y() < copy[1].y())
            {
                tl = copy[1];
                bl = copy[0];
            }
            else
            {
                tl = copy[0];
                bl = copy[1];
            }

            if (copy[2].y() < copy[3].y())
            {
                tr = copy[3];
                br = copy[2];
            }
            else
            {
                tr = copy[2];
                br = copy[3];
            }
        }

        void CLTProfileView::paintEvent(QPaintEvent *event)
        {
            OI_F(CLTProfileView);

            QGraphicsView::paintEvent(event);

            QPainter painter(viewport());
            painter.setPen(QPen(Qt::red, 1, Qt::DashLine));

            for (int i = 0; i < d->_defectTools.size(); i++)
            {
                if (d->_defectTools[i].data.size() != 4)
                {
                    continue;
                }

                for (int k = 0; k < d->_defectTools[i].data.size(); k++)
                {
                    painter.drawLine(d->_chart->mapToScene(d->_chart->mapToPosition(d->_defectTools[i].data[k])), d->_chart->mapToScene(d->_chart->mapToPosition(d->_defectTools[i].data[(k + 1) % 4])));
                }

                QFont f = font();

                QPointF tl;
                QPointF tr;
                QPointF bl;
                QPointF br;
                confirmCorner(d->_defectTools[i].data, tl, tr, bl, br);

                tl = d->_chart->mapToScene(d->_chart->mapToPosition(tl));
                tr = d->_chart->mapToScene(d->_chart->mapToPosition(tr));
                bl = d->_chart->mapToScene(d->_chart->mapToPosition(bl));
                br = d->_chart->mapToScene(d->_chart->mapToPosition(br));
                QPointF dir = bl - tl;
                float range = qSqrt(qPow(dir.x(), 2) + qPow(dir.y(), 2));
                dir = QPointF(dir.x() / range, dir.y() / range);
                float angle = atan2(tr.y() - tl.y(), tr.x() - tl.x()) * 180 / CV_PI;

                f.setPointSizeF(LT360X::instance()->defectToolNameFontSize());
                painter.setFont(f);
                painter.translate(tl - dir * 3);
                painter.rotate(angle);
                painter.drawText(QPointF(0, 0), QString::number(d->_defectTools[i].errorXY, 'f', 2) + "x" + QString::number(d->_defectTools[i].errorH, 'f', 2));
                painter.resetMatrix();

                if (d->_defectTools.size() == d->_defectCount.size())
                {
                    if (range / 2 > LT360X::instance()->defectToolCountFontSize())
                    {
                        bl = tl + dir * range / 2;
                    }
                    else
                    {
                        bl = tl + dir * LT360X::instance()->defectToolCountFontSize();
                    }
                    f.setPointSizeF(LT360X::instance()->defectToolCountFontSize());
                    painter.setFont(f);
                    painter.translate(bl);
                    painter.rotate(angle);
                    painter.drawText(QPointF(0, 0), QString("%1个").arg(d->_defectCount[i]));
                    painter.resetMatrix();
                }
            }

            if (!d->_displayName.isNull())
            {
                int spacing = 8;
                int borderSpacing = 5;
                int borderWidth = 3;

                auto font = painter.font();
                font.setPointSizeF(28);
                painter.setFont(font);

                QFontMetrics fontMetric(font);
                auto nameBoundingBox = fontMetric.boundingRect(d->_displayName);

                int displacementX = (viewport()->rect().width() - nameBoundingBox.width()) * 0.5;
                int displacementY = d->_chart->plotArea().topLeft().y() + nameBoundingBox.height();

                nameBoundingBox = nameBoundingBox.adjusted(displacementX, displacementY, displacementX, displacementY);
                nameBoundingBox = nameBoundingBox.adjusted(-spacing, borderSpacing, spacing, borderSpacing + spacing);

                painter.setPen(Qt::white);
                painter.drawText(nameBoundingBox, Qt::AlignCenter, d->_displayName);

                painter.setPen(QPen(Qt::darkGray, borderWidth));
                painter.drawRect(nameBoundingBox);
            }

        }

        void CLTProfileView::wheelEvent(QWheelEvent *event)
        {
            OI_F(CLTProfileView);
            if(event->delta() > 0)
            {
                d->_chart->zoom(1.1);
            }
            else
            {
                d->_chart->zoom(10.0 / 11);
            }
        }

        void CLTProfileView::drawDefectBoxes(const QList<LTBase::DefectTool>& tools, const QList<int>& value)
        {
            OI_F(CLTProfileView);

            d->_defectTools = tools;
            d->_defectCount = value;
        }

        void CLTProfileView::setName(const QString& name)
        {
            OI_F(CLTProfileView);
            d->_displayName = name;
            update();
        }


        class MainProfileViewImpl : public Impl<MainProfileView>
        {
        public:
            void init();
            void exit();
        };

        void MainProfileViewImpl::init()
        {
            OI_Q(MainProfileView);
            
            QObject::connect(q->chart(), &Appl::Chart::settingChanged, q, &MainProfileView::onSaveSetting);
        }

        void MainProfileViewImpl::exit()
        {

        }

        MainProfileView::MainProfileView(QWidget* parent)
            : CLTProfileView(parent)
        {
            OI_I(MainProfileView)->init();
        }

        MainProfileView::~MainProfileView()
        {
            OI_I(MainProfileView)->exit();
        }

        void MainProfileView::onSaveSetting()
        {
            chart()->saveSetting(LT360X::instance()->projectDir + "/" + LT360X::instance()->formula() + "/" + "config.ini");
        }

        void MainProfileView::mouseMoveEvent(QMouseEvent* event)
        {
            OI_F(MainProfileView);

            if (event->buttons() & Qt::MiddleButton)
            {
                chart()->saveSetting(LT360X::instance()->projectDir + "/" + LT360X::instance()->formula() + "/" + "config.ini");
            }

            CLTProfileView::mouseMoveEvent(event);
        }

        void MainProfileView::mouseDoubleClickEvent(QMouseEvent* event)
        {
            OI_F(MainProfileView);

            if (event->button() & Qt::MouseEventCreatedDoubleClick)
            {
                chart()->saveSetting(LT360X::instance()->projectDir + "/" + LT360X::instance()->formula() + "/" + "config.ini");
            }

            CLTProfileView::mouseDoubleClickEvent(event);
        }

        void MainProfileView::hideEvent(QHideEvent* event)
        {
            OI_F(MainProfileView);

            if (chart())
            {
                chart()->setVisible(false);
            }
        }

        void MainProfileView::showEvent(QShowEvent* event)
        {
            OI_F(MainProfileView);

            if (chart())
            {
                chart()->setVisible(true);

            }
        }

        void MainProfileView::wheelEvent(QWheelEvent* event)
        {
            OI_F(MainProfileView);
            CLTProfileView::wheelEvent(event);
            chart()->saveSetting(LT360X::instance()->projectDir + "/" + LT360X::instance()->formula() + "/" + "config.ini");
        }
    }
}
